﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <ctype.h>
#include <assert.h>


/*********************************************************
字符分类函数
C语⾔中有⼀系列的函数是专⻔做字符分类的，也就是⼀个字符是属于什么类型的字符的。
这些函数的使⽤都需要包含⼀个头⽂件是 ctype.h
*****************************************************/


//islower 是能够判断参数部分的 c 是否是⼩写字⺟的。
//通过返回值来说明是否是⼩写字⺟，如果是⼩写字⺟就返回⾮0的整数，如果不是⼩写字⺟，则返回0。
//int main()
//{
//	int ret = islower('A');
//	printf("%d\n", ret);
//	ret = islower('a');
//	printf("%d\n", ret);
//	
//	ret = islower('0');
//	printf("%d\n", ret);
//	
//	return 0;
//}

////isxdigit 判定是否为16进制的 包括十进制数字字符 小写字母 大写字母a~f 返回的值是非0值
//int main()
//{
//	int ret = isxdigit('9');
//	printf("%d\n", ret);//128 
//	
//	return 0;
//}

//写一个代码，将字符串中的小写字母转大写，其他字符不变

//islower 是能够判断参数部分的 c 是否是⼩写字⺟的。
//通过返回值来说明是否是⼩写字⺟，如果是⼩写字⺟就返回⾮0的整数，如果不是⼩写字⺟，则返回0。
//int main()
//{
//	char arr[] = "I am a Student.";
//	//            0123
//	int i = 0;
//	while (arr[i] != '\0')//去遍历这个字符串 不等于\0就一直循环
//	{
//		if (islower(arr[i]))//如果遍历过程中 字符串中有一个是小写就会返回非0值 就会进入下边的代码
//		{
//			arr[i] -= 32;//小写字母ASCII - 32 = 大写字母ASCII码值 就实现小写变为大写
//		}
//		i++;//字符串开始逐个遍历
//	}
//
//	printf("%s\n", arr);
//	
//	return 0;
//}
//

/********************************************************************************************
字符转换函数
C语⾔提供了2个字符转换函数：
int tolower(int c); //将参数传进去的⼤写字⺟转⼩写 
int toupper(int c); //将参数传进去的⼩写字⺟转⼤写
上⾯的代码，我们将⼩写转⼤写，是 - 32完成的效果，有了转换函数，就可以直接使⽤ tolower 函数。
*********************************************************************************************/
//
//int main()
//{
//	char ch = toupper('a');//将小写转换为大写
//	printf("%c\n", ch);
//
//	ch = tolower('A');//将大写转换为小写
//	printf("%c\n", ch);
//
//	return 0;
//}
//
//
//int main()
//{
//	char arr[] = "I am a Student.";
//	//            0123
//	int i = 0;
//	while (arr[i] != '\0')
//	{
//		if (islower(arr[i]))// 可以用这句 if(arr[i]>='a' && arr[i]<='z') 来判断是否为小写字母 而且还是在a~z当中
//		{
//			arr[i] = toupper(arr[i]);// -= 32  toupper函数就实现了在遍历的过程中将小写转换为大写 
//		}
//		i++;
//	}
//
//	printf("%s\n", arr);
//	
//	return 0;
//}

#include <string.h>

//strlen函数统计字符串个数
//字符串以 '\0' 作为结束标志，strlen函数返回的是在字符串中 '\0' 前⾯出现的字符个数（不包含 '\0' )。
//注意函数的返回值为 size_t，是⽆符号的（ 易错 ）

//int main()
//{
//	//char arr[] = "abcdef";
//	char arr[] = {'a', 'b','c'};
//
//	size_t len = strlen(arr);
//
//	printf("%zd\n", len);
//
//	return 0;
//}

//
//int main()
//{
//	if (strlen("abc") - strlen("abcdef") > 0)//3 - 6 = -3  因为返回值只能是无符号整数 符号位无效 补码非常大 大于0
//	{
//		printf(">\n");//得到这个 因为返回值只能是无符号整数
//	}
//	else
//	{
//		printf("<=\n");
//	}
//
//	return 0;
//}
//



//int main()
//{
//	if ((int)strlen("abc") - (int)strlen("abcdef") > 0)//把两个字符串强制转换为int型也可以进行计算输出正确结果 但是麻烦
//	{
//		printf(">\n");
//	}
//	else
//	{
//		printf("<=\n");
//	}
//
//	return 0;
//}


//int main()
//{
//	if (strlen("abc") > strlen("abcdef"))//直接比字符串个数大小就可以
//	{
//		printf(">\n");
//	}
//	else
//	{
//		printf("<=\n");
//	}
//
//	return 0;
//}

/******************************************************************************

*******************************************************************************/
/******************************************************************************
模拟 strlen 函数的3种使用
*******************************************************************************/

//方法1-计数器
//size_t my_strlen(const char* str) //加上const 放在*str 前面  修饰的是*str   *str就不可以被改变了 增加其健壮性
//{
//	int count = 0;
//	assert(str != NULL);//断言 避免野指针
//	while (*str != '\0')
//	{
//		count++;
//		str++;
//	}
//	return count;
//}

//2.指针-指针
//size_t my_strlen(const char* str)
//{
//	const char* start = str;//把str起始地址给start  start此时指向字符串的起始地址 数组名是数数组首元素地址
//	assert(str != NULL);
//
//	while (*str)//这样写也可以 碰到\0也是非0值 表达式不为1循环停止
//	{
//		str++;
//	}
//	return str - start;//遍历后的地址减去起始地址可以得到元素个数
//}


//3. 递归的方式

//size_t my_strlen(const char* str)
//{
//	assert(str != NULL);
//
//	if (*str != '\0')
//		return 1 + my_strlen(str + 1);
//	else
//		return 0;
//}
//
//int main()
//{
//	char arr[] = "abcdef";
//	size_t len = my_strlen(arr);
//	printf("%zd\n", len);
//
//	return 0;
//}

/******************************************************************************
strcpy 函数的使用  
拷贝函数
• 源字符串必须以 '\0' 结束。
• 会将源字符串中的 '\0' 拷⻉到⽬标空间。
• ⽬标空间必须⾜够⼤，以确保能存放源字符串。
*******************************************************************************/


//#include <string.h>
//
//int main()
//{
//	char arr1[] = "hello world";
//	char arr2[20] = {0};//1个元素
//	
//	strcpy(arr2, arr1);
//	printf("%s\n", arr2);
//
// 	return 0;
//}


//#include <string.h>
//
//int main()
//{
//	char arr1[] = "hello world";
//	char* p = "xxxxxxxxxxxxxxxxxxxx";//常量字符串-不能修改
//	strcpy(p, arr1);//err
//	printf("%s\n", p);
//
//	return 0;
//}

//
//模拟实现一个strcpy?
//
//
//为的是实现链式访问
//strcpy函数返回的是目标空间的起始地址
//
/******************************************************************************
模拟 strcpy 函数的使用
********************************************************************************/
//第一种

//char* my_strcpy(char* arr2, const char* arr1)
//{
//	assert(arr1 != NULL);//断言 arr1不等于0 野指针
//	char* tmp = arr2;//把起始地址存在另一个指针变量
//	while (*arr1)//判断arr1是否到\0
//	{
//		*arr2 = *arr1; //解引用将源头赋予目标值
//		arr1++;//相当于地址加1 步进长度是1个字节 因为是char型
//		arr2++;
//	}
//    *arr2 = *arr1;//把\0拷贝过去
//	return tmp;//返回目标空间的起始地址 arr2赋予了tmp起始地址 
//}

//第二种
//char* my_strcpy(char* arr2, const char* arr1)
//{
// // 	assert(arr1 && arr2);//断言 arr1与arr2不等于0 野指针
//	char* tmp = arr2;//把起始地址存在另一个指针变量
//	while (*arr2++ = *arr1++);//解引用将源头赋予目标值 这个更好的灵活性 因为不断++到后面\0 就是0值 循环就会停止 先解引用 再++ 因为是后置
//	*arr2 = *arr1;//把\0拷贝过去
//	return tmp;//返回目标空间的起始地址 arr2赋予了tmp起始地址 
//}


//int main()
//{
//	char arr1[] = "abcdef";
//	char arr2[10] = { 0 };
//
//	char* p = my_strcpy(arr2, arr1);//接收 返回目标起始地址 就是arr2 
//
//	my_strcpy(arr2, arr1);
//	printf("%s\n", arr2);//打印arr2字符串
//	printf("%s\n", p);//打印返回值返回目标起始地址  就是arr2 
//	printf("%p\n", &arr2);//取出arr的地址 打印出来
//	printf("%s\n", my_strcpy(arr2, arr1));//直接打印返回值地址里面的值
//	return 0;
//}
// 
/******************************************************************************
模拟 strcat 函数的使用
可以将函数拷贝追加到另一个目标函数后面
********************************************************************************/

char* my_strcat(char* arr1, const char* arr2)
{
	char* ret = arr1;
	assert(arr1 && arr2);//断言 arr1与arr2不等于0 野指针
	while (*arr1)//去找是否等于\0 没找到arr1就一直加加 
		arr1++;
	while (*arr1++ = *arr2++);//此时arr1已经找到\0了 arr1处于的位置就是字符串末尾 然后将arr2源头加到目标后边
	return ret;
}

int main()
{
		char arr1[20] = "helloxxxx";
		char arr2[] = "word";
		my_strcat(arr1, arr2);
		printf("%s\n", arr1);
		return 0;
}


/******************************************************************************
模拟 strcmp 函数的使用
◦ 第⼀个字符串⼤于第⼆个字符串，则返回⼤于0的数字
◦ 第⼀个字符串等于第⼆个字符串，则返回0
◦ 第⼀个字符串⼩于第⼆个字符串，则返回⼩于0的数字
◦ 那么如何判断两个字符串？ ⽐较两个字符串中对应位置上字符ASCII码值的⼤⼩。
********************************************************************************/

//第一种
//int my_strcmp(const char* arr1, const char* arr2)
//{
//	assert(arr1 && arr2);
//	while (*arr1 == *arr2)
//	{
//		if (*arr1 == '\0')
//			return 0;
//		arr1++;
//		arr2++;
//	}
//	if (*arr1 > *arr2)
//		return 1;
//	else
//		return -1;
//}

//第二种

//int my_strcmp(const char* arr1, const char* arr2)
//{
//	assert(arr1 && arr2);
//	while (*arr1 == *arr2)//循环判断两个字符串是否相等 相等就进去循环
//	{
//		if (*arr1 == '\0')//如果找到\0 就代表已经找完了 因为前面循环已经相等了才进来 arr1和arr2此时是相等的
//			return 0;//所以就返回0
//		arr1++;//循环找下一个 是不是相等的
//		arr2++;
//	}
//	return (*arr1 - *arr2);//得出相差的值返回回去
//}
//
//int main()
//{
//		char arr1[] = "abq";
//		char arr2[] = "word";
//		int ret = my_strcmp(arr1, arr2);
//		printf("%d\n", ret);
//		return 0;
//}

//带个 n 就是可以限定字符串个数 没有就是不能限定
/********************************************
strncpy 函数的使⽤
拷⻉num个字符从源字符串到⽬标空间。
如果源字符串的⻓度⼩于num，则拷⻉完源字符串之后，在⽬标的后边追加0，直到num个。
**********************************************/
//int main()
//{
//	char arr1[20] = "abcdef";
//	char arr2[20] = "xxxxxxxxxxx";
//
//	strncpy(arr2, arr1, 8);
//	printf("%s\n", arr2);
//
//	return 0;
//}
//

/**********************************************************************************************************
 strncat 函数的使⽤
（将source指向字符串的前num个字符追加到destination指向的字符串末尾，再追加⼀个 \0 字符）。
如果source 指向的字符串的⻓度⼩于num的时候，只会将字符串中到\0 的内容追加到destination指向的字符串末尾
**********************************************************************************************************/
//int main()
//{
//	char arr1[20] = "abcdef\0yyyyyyyy";
//	char arr2[20] = "xxxxxxxx";
//
//	strncat(arr1, arr2, 3);
//
//	printf("%s\n", arr2);
//
//	return 0;
//}
//
/**********************************************************************************************************
* strncmp 函数的使⽤
⽐较str1和str2的前num个字符，如果相等就继续往后⽐较，最多⽐较num个字⺟，如果提前发现不⼀
样，就提前结束，⼤的字符所在的字符串⼤于另外⼀个。如果num个字符都相等，就是相等返回0.
**********************************************************************************************************/
//
//int main()
//{
//	char arr1[20] = "abcdef";
//	char arr2[20] = "ab";
//
//	int ret = strncmp(arr1, arr2, 4);
//
//	printf("%d\n", ret);
//
//	return 0;
//}
//
//

/*************************************************************************************
* strtok 函数的使⽤
• sep参数指向⼀个字符串，定义了⽤作分隔符的字符集合
• 第⼀个参数指定⼀个字符串，它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标记。
• strtok函数找到str中的下⼀个标记，并将其⽤ \0 结尾，返回⼀个指向这个标记的指针。
（注：strtok函数会改变被操作的字符串，所以被strtok函数切分的字符串⼀般都是临时拷⻉的内容并且可修改。）

• strtok函数的第⼀个参数不为NULL ，函数将找到str中第⼀个标记，strtok函数将保存它在字符串中的位置。
• strtok函数的第⼀个参数为 NULL ，函数将在同⼀个字符串中被保存的位置开始，查找下⼀个标记。
• 如果字符串中不存在更多的标记，则返回 NULL 指针
*****************************************************************************/

//int main()
//{
//	char arr[] = "zpengwei@yeah.net@hehe.haha#ttttt";
//	char buf[256] = { 0 };
//	strcpy(buf, arr);//"zpengwei\0yeah.net"//把arr拷贝到buf数组 不影响原本arr的字符串数组
//	//char sep[] = "@.";
//	char* sep = "@.";//给分隔符标记 就会把这个分割符变成\0 也会标记这个位置 保存起来 下次从这个标记开始往后找下一个标记
//	char* ret = NULL;//因为返回值是char* 返回值为空指针
//	for (ret = strtok(buf, sep); ret != NULL; ret = strtok(NULL, sep))//返回值就是开始到分割符之间的字符 返回值不为空指针 下一次就从上次记忆的空指针开始往后找
//	{ 
//		printf("%s\n", ret);
//	}

	//char* ret = strtok(buf, sep);
	//printf("%s\n", ret);
	//ret = strtok(NULL, sep);
	//printf("%s\n", ret);
	//ret = strtok(NULL, sep);
	//printf("%s\n", ret);

//	return 0;
//}